home *** CD-ROM | disk | FTP | other *** search
/ Almathera Ten Pack 3: CDPD 3 / Almathera Ten on Ten - Disc 3: CDPD3.iso / scope / 101-125 / scopedisk108 / unshar / src / unshar.c < prev    next >
C/C++ Source or Header  |  1995-03-19  |  30KB  |  799 lines

  1. /*
  2.  *                     Unshar V1.2 (C) Copyright Eddy Carroll 1990
  3.  *                     ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
  4.  * Usage: Unshar {-overwrite} {-nosort} <filename> ...
  5.  *
  6.  * Extracts files from a SHAR'd archive.
  7.  *
  8.  * This utility has a few advantages over the version of SH on Fish Disk 92.
  9.  * For a start, it doesn't crash if it gets a slightly unusual format! It
  10.  * also has a (limited) capability for extracting files from shar archives
  11.  * which use 'SED' rather than 'CAT' (typically, this is done so that
  12.  * each line in the file may be prefixed with an 'X' or similar, so that
  13.  * indentation is preserved). Unshar will spot 'SED' lines, and treat them
  14.  * the same as 'CAT' (allowing for different parameters of course) with
  15.  * the exception that any leading characters matching the string specified
  16.  * in the SED command are discarded.
  17.  *
  18.  * Unshar checks files being extracted to see if they are to be stored
  19.  * within a sub-directory. If they are, and the sub-directory does not
  20.  * already exist, it is created.
  21.  *
  22.  * One other small addition is that any filenames which are prefixed with
  23.  * the characters "./" have these characters removed. Some shar files
  24.  * use this prefix to ensure that the files are stored in the current
  25.  * directory.
  26.  *
  27.  * Files are extracted into the current directory. As each file is extracted,
  28.  * an appropriate message is printed on the screen. If the file already
  29.  * exists, the user is warned and given the chance to avoid overwriting it
  30.  * "Overwrite file (Yes/No/All)? ". The default is Yes. If All is selected,
  31.  * then this prompt is supressed for the rest of the current file. It may
  32.  * be disabled for all the files by specifying the -o switch on the
  33.  * command line.
  34.  *
  35.  * By default, unshar will do a `prescan' over all the files listed, looking
  36.  * at the first few lines of each for a Subject: line. If one is found, then
  37.  * it examines it for Issue numbers and Part numbers, and unshars those files
  38.  * having the lowest numbers first. This results in the shar files being
  39.  * extracted in the correct order, regardless of what order they were listed
  40.  * in on the command line. You can override this behaviour and unshar files
  41.  * in the command line order by specifying the -n switch.
  42.  * 
  43.  * DISTRIBUTION
  44.  * I retain copyright rights to this source code, though it may be _freely_
  45.  * distributed. The executable file created from this source code is in
  46.  * the Public Domain and may be distributed without any restrictions.
  47.  *
  48.  *
  49.  * N.b. The code is starting to look a bit messy; could be it will get
  50.  *      a complete overhaul for the next revision.
  51.  *
  52.  */
  53.  
  54. /* Compiles under Lattice V5.04 */
  55.  
  56. #ifndef LATTICE_50 
  57. #include "system.h"
  58. #endif
  59.  
  60. #define YES                    1
  61. #define NO                     0
  62. #define CR                     '\015'
  63. #define EOL                    '\012'
  64. #define SINGLEQUOTE '\''
  65. #define DOUBLEQUOTE '\042'
  66. #define MAXSTRING      512             /* Maximum length of input line */
  67.  
  68. /*
  69.  *             New handler for Ctrl-C. Checks if CTRL-C received, and if it has,
  70.  *             sets the global CtrlC variable to true.
  71.  */
  72. #define chkabort() (CtrlC |= ((SetSignal(0,0) & SIGBREAKF_CTRL_C)))
  73.  
  74.  
  75. char HelpMsg[] = "\
  76. Unshar by Eddy Carroll 1990 Public Domain, extracts files from shar archives.\
  77. \n\
  78. Usage: unshar {-overwrite} {-nosort} <filename> ...\n";
  79.  
  80. char DiskMsg[]  = "Unshar aborted - Disk write error (disk full?)\n";
  81. char ErrorMsg[] = "Unshar: Invalid CAT or SED command at line ";
  82.  
  83. int linenum;
  84. int CtrlC = NO;
  85.  
  86. /*
  87.  * --------------------------------------------------------------------------
  88.  * The following block may be removed `as-is' and used in other programs.
  89.  * It provides basic buffered i/o on two files, an input file and an output
  90.  * file. It also provides output to the current standard output via
  91.  * print. Buffering is done using buffers of size MAXBUF.
  92.  *
  93.  * The following routines are provided:
  94.  *
  95.  * getc() returns an integer corresponding to the next character read from
  96.  * infile, or EOF if the end of file has been reached.
  97.  *
  98.  * putc(c) outputs a character to outfile. If a diskerror occurs, the global
  99.  * diskerror is set to YES, and all further diskwrites are ignored.
  100.  *
  101.  * getline() returns a pointer to a string containing the next line
  102.  * read in from infile. getline() also checks for CTRL-C via chkabort()
  103.  * 
  104.  * putline(s) outputs a string to outfile, returning non-zero if an
  105.  * error occurred during the write.
  106.  *
  107.  * flushin() resets getc() and getline() for input from a new file
  108.  *
  109.  * flushout() flushes output buffer; call prior to closing output file.
  110.  *
  111.  * input() returns a pointer to a string containing a line from stdin.
  112.  *
  113.  * print(s) prints a message on standard output.
  114.  *
  115.  * print3(s1,s2,s3) outputs three strings on standard output.
  116.  *
  117.  * numtostr(n) returns a pointer to the ascii representation of n.
  118.  *
  119.  * Special Notes
  120.  * ~~~~~~~~~~~~~
  121.  * You should ensure that you use the filenames 'infile' and 'outfile'
  122.  * when you are opening the input and output files in main(). Also,
  123.  * do not #define EOF or MAXBUF elsewhere in your program.
  124.  *
  125.  */
  126.  
  127. #define EOF            -1
  128. #define MAXBUF 10000
  129.  
  130. BPTR infile, outfile;
  131. LONG maxin = MAXBUF, maxout = MAXBUF, inbuf = MAXBUF, outbuf = 0;
  132. unsigned char inbuffer[MAXBUF], outbuffer[MAXBUF];
  133. int diskerror = NO;
  134.  
  135. /*
  136.  *             int getc()
  137.  *             ----------
  138.  *             Returns next character from infile, or EOF if end of file.
  139.  *
  140.  *             Replaced by a macro to improve performance. Original function was:
  141.  *
  142.  *             int getc()
  143.  *             {
  144.  *                     if (!maxin)
  145.  *                             return (EOF);
  146.  *
  147.  *                     if (inbuf >= maxin) {
  148.  *                             maxin = Read(infile, inbuffer, MAXBUF);
  149.  *                             inbuf = 0;
  150.  *                             if (!maxin)
  151.  *                                     return (EOF);
  152.  *                     }
  153.  *                     return (inbuffer[inbuf++]);
  154.  *             }
  155.  *
  156.  */
  157. #define IF(x,y,z) ((x) ? (y) : (z))
  158.  
  159. #define getc() IF(!maxin, EOF, \
  160.                                IF(inbuf >= maxin, ( \
  161.                                        inbuf = 0, maxin = Read(infile, inbuffer, MAXBUF), \
  162.                                        IF(!maxin, EOF, inbuffer[inbuf++]) \
  163.                                ), inbuffer[inbuf++])) \
  164.  
  165. /* 
  166.  *             Prepares getc() for input from a new file
  167.  */
  168. #define flushin() (maxin = MAXBUF, inbuf = MAXBUF)
  169.  
  170. /*
  171.  *             putc(ch)
  172.  *             --------
  173.  *             Outputs character ch to disk. If a diskerror is detected, then all
  174.  *             further output is ignored and the global diskerror is set to YES.
  175.  *
  176.  *             Replaced by a macro for performance reasons. Original function was:
  177.  *
  178.  *             void putc(ch)
  179.  *             int ch;
  180.  *             {
  181.  *                     if (ch == EOF)
  182.  *                             maxout = outbuf;
  183.  *                     else
  184.  *                             outbuffer[outbuf++] = ch;
  185.  *             
  186.  *                     if (outbuf >= maxout) {
  187.  *                             if (!diskerror && Write(outfile, outbuffer, maxout) == -1)
  188.  *                                     diskerror = YES;
  189.  *                             outbuf = 0;
  190.  *                             maxout = MAXBUF;
  191.  *                     }
  192.  *             }
  193.  */
  194. #define flushout() (maxout = outbuf, \
  195.                                        IF(!diskerror && Write(outfile, outbuffer, maxout) == -1, \
  196.                                                diskerror = YES, \
  197.                                                0), \
  198.                                        outbuf = 0, maxout = MAXBUF)
  199.  
  200. #define putc(ch) (outbuffer[outbuf++] = ch, \
  201.                                  IF(outbuf >= maxout, \
  202.                                        (IF (!diskerror && \
  203.                                                        Write(outfile, outbuffer, maxout) == -1, \
  204.                                                diskerror = YES, \
  205.                                                0), \
  206.                                         outbuf = 0, maxout = MAXBUF), \
  207.                                        0))
  208.  
  209. /*
  210.  *             print(s)
  211.  *             --------
  212.  *             Outputs a message to std output
  213.  */
  214. void print(s)
  215. char *s;
  216. {
  217.        Write(Output(),s,strlen(s));
  218. }
  219.  
  220. /*
  221.  *             print3()
  222.  *             --------
  223.  *             Outputs three strings to std output.
  224.  *             Useful for sequences like print3("string", variable, "string");
  225.  */
  226. void print3(s1,s2,s3)
  227. char *s1,*s2,*s3;
  228. {
  229.        print(s1);
  230.        print(s2);
  231.        print(s3);
  232. }
  233.  
  234. /*
  235.  *             getline()
  236.  *             ---------
  237.  *             Reads in a line from current infile into string, and returns a
  238.  *             pointer to that string. Returns NULL if EOF encountered.
  239.  */
  240. char *getline()
  241. {
  242.        register int ch, i = 0;
  243.        static char line[MAXSTRING];
  244.  
  245.        ch = getc();
  246.        if (ch == EOF)
  247.                return (NULL);
  248.  
  249.        while (i < (MAXSTRING-1) && ch != EOF && ch != EOL) {
  250.                line[i++] = ch;
  251.                ch = getc();
  252.        }
  253.  
  254.        line[i] = '\0';
  255.        linenum++;
  256.        chkabort();
  257.        return (line);
  258. }
  259.  
  260. /*
  261.  *             putline()
  262.  *             ---------
  263.  *             Outputs a string to the current output file (terminating it with LF).
  264.  *             Returns 0 for success, non-zero for disk error
  265.  */
  266. int putline(s)
  267. char *s;
  268. {
  269.        while (*s)
  270.                putc(*s++);
  271.        putc(EOL);
  272.        return (diskerror);
  273. }
  274.  
  275. /*
  276.  *             input()
  277.  *             -------
  278.  *             Reads a line from keyboard and returns pointer to it
  279.  */
  280. char *input()
  281. {
  282.        static char s[80];
  283.  
  284.        s[Read(Input(),s,75)] = '\0';
  285.        return(s);
  286. }
  287.  
  288. /*
  289.  *             numtostr()
  290.  *             ----------
  291.  *             Converts integer to string and returns pointer to it.
  292.  */
  293. char *numtostr(n)
  294. int n;
  295. {
  296.        static char s[20];
  297.        int i = 19;
  298.  
  299.        s[19] = '\0';
  300.        if (n)
  301.                while (n)
  302.                        s[--i] = '0' + (n % 10), n /= 10;
  303.        else
  304.                s[--i] = '0';
  305.        return(&s[i]);
  306. }
  307.  
  308. /*
  309.  *             --------------------* End of Buffered IO routines *-----------------
  310.  */
  311.  
  312. /*
  313.  *             index()
  314.  *             -------
  315.  *             Like standard Unix index(), but skips over quotes if skip == true.
  316.  *             Also skips over chars prefixed by a \. Returns pointer to first
  317.  *             occurance of char c inside string s, or NULL.
  318.  */
  319. char *index(s,c,skip)
  320. char *s,c;
  321. int skip;
  322. {
  323.        register char *p = s;
  324.        register int noquotes = YES, literal = NO;
  325.  
  326.        while (*p) {
  327.                if (literal) {
  328.                        literal = NO;
  329.                        p++;
  330.                } else {
  331.                        if (skip && ((*p == SINGLEQUOTE) || (*p == DOUBLEQUOTE)))
  332.                                noquotes = !noquotes;
  333.                        if (noquotes && (*p == c))
  334.                                return(p);
  335.                        literal = (*p == '\\');
  336.                        p++;
  337.                }
  338.        }
  339.        return (NULL);
  340. }
  341.  
  342. /*
  343.  *             getname()
  344.  *             ---------
  345.  *             Extracts a string from start of string s1 and stores it in s2.
  346.  *             Leading spaces are discarded, and quotes, if present, are used to
  347.  *             indicate the start and end of the filename. If mode is MODE_FILE,
  348.  *             then if the name starts with either './' or '/', this prefix is
  349.  *             stripped. This doesn't happen if the mode is MODE_TEXT. A pointer
  350.  *             to the first character after the string in s1 is returned. In
  351.  *             addition, any characters prefixed with are passed through without
  352.  *             checking.
  353.  */
  354.  
  355. #define MODE_FILE 1
  356. #define MODE_TEXT 2
  357.  
  358. char *getname(s1,s2,mode)
  359. char *s1,*s2;
  360. {
  361.        char endchar = ' ';
  362.  
  363.        while (*s1 == ' ')
  364.                s1++;
  365.  
  366.        if (*s1 == SINGLEQUOTE || *s1 == DOUBLEQUOTE)
  367.                endchar = *s1++;
  368.  
  369.        if (mode == MODE_FILE) {
  370.                if (s1[0] == '.' && s1[1] == '/')
  371.                        s1 += 2;
  372.                while (*s1 == '/')
  373.                        s1++;
  374.        }
  375.  
  376.        while (*s1 && *s1 != endchar) {
  377.                if (*s1 == '\\' && *(s1+1))
  378.                        s1++;
  379.                *s2++ = *s1++;
  380.        }
  381.        *s2 = '\0';
  382.  
  383.        if (*s1 == endchar)
  384.                return(++s1);
  385.        else
  386.                return(s1);
  387. }
  388.  
  389.  
  390. /*
  391.  *             checkfordir()
  392.  *             -------------
  393.  *             Checks filename to see if it is inside a subdirectory. If it is,
  394.  *             then checks if subdirectory exists, and creates it if it doesn't.
  395.  */
  396. void checkfordir(filename)
  397. char *filename;
  398. {
  399.        char dir[80], *p;
  400.        int i, x;
  401.        BPTR dirlock;
  402.  
  403.        p = filename;
  404.  
  405.        while (p = index(p, '/', 1)) {
  406.  
  407.                /* Dir exists, so copy dir part of filename into dir name area */
  408.  
  409.                x = p - filename;
  410.                for (i = 0; i < x; i++)
  411.                        dir[i] = filename[i];
  412.                dir[i] = '\0';
  413.  
  414.                /* Now, see if directory exists, if not then create */
  415.                if ((dirlock = Lock(dir,ACCESS_READ)) == NULL) {
  416.                        dirlock = CreateDir(dir);
  417.                        if (dirlock) {
  418.                                print3("Creating directory ", dir, "\n");
  419.                        }
  420.                }
  421.                if (dirlock)
  422.                        UnLock(dirlock);
  423.  
  424.                p++;
  425.        }
  426. }
  427.  
  428. /*
  429.  *             unshar()
  430.  *             --------
  431.  *             Extracts all stored files from a shar file. Returns zero for success,
  432.  *             non-zero if a disk error occurred. If echofilename is non-zero, then
  433.  *             the name of each shar file is output before unsharing it. If
  434.  *             overwrite is non-zero, then existing files are overwritten without
  435.  *             any warning. If title is non-NULL, then it points to a string
  436.  *             which is printed out before any files are extracted.
  437.  */
  438. int unshar(sharfile, title, echofilename, overwrite)
  439. char *sharfile, *title;
  440. int echofilename, overwrite;
  441. {
  442.        register char *s, *p;
  443.        char endmarker[100], filename[100],sedstring[100];
  444.        int endlen, stripfirst, startfile, found = NO, err = NO, skip, sedlen;
  445.        int append;
  446.        BPTR filelock;
  447.  
  448.        if ((infile = Open(sharfile, MODE_OLDFILE)) == NULL) {
  449.                print3("Can't open file ", sharfile, " for input\n");
  450.                return(1);
  451.        }
  452.  
  453.        linenum = 0;
  454.        if (echofilename)
  455.                print3("\033[7m Shar file: ", sharfile, " \033[0m\n");
  456.        if (title)
  457.                print(title);
  458.  
  459.        while (!err && !CtrlC && (s = getline()) != NULL) {
  460.                startfile = NO;
  461.                if (strncmp(s,"cat ",4) == 0) {
  462.                        startfile  = YES;
  463.                        stripfirst = NO;
  464.                }
  465.                if (strncmp(s,"sed ",4) == 0) {
  466.                        startfile  = YES;
  467.                        stripfirst = YES;
  468.                        sedlen = 0;
  469.                        /*
  470.                         *              Note - tons of sanity checks done here to ensure that a
  471.                         *              sed line of the form:
  472.                         *
  473.                         *                      sed >s/somefile <<'endmarker' -e 's/X//'
  474.                         *
  475.                         *              Will be interpreted correctly.
  476.                         */
  477.  
  478. #define ISPREFIX(ch)   (ch == DOUBLEQUOTE || ch == SINGLEQUOTE || ch == ' ')
  479. #define ISMETA(ch)             (ch == '<' || ch == '>')
  480. #define ISOK(s)                        (s[1] == '/' && ISPREFIX(s[-1]) && !ISMETA(s[-2]))
  481.  
  482.                        p = s;
  483.                        while ((p = index(p,'s',0)) != NULL && !ISOK(p))
  484.                                p++;
  485.                        if (p != NULL) {
  486.                                p += 2;                         /* Skip over the 's/' bit       */
  487.                                if (*p == '^')          /* Skip past starting char      */
  488.                                        p++;
  489.                                while (*p && *p != '/')
  490.                                        sedstring[sedlen++] = *p++;
  491.                        } 
  492.                }
  493.  
  494.                if (startfile) {        
  495.                        if (found == NO) {
  496.                                found = YES;
  497.                        }
  498.                        if ((p = index(s,'>',1)) == NULL) {
  499.                                print3(ErrorMsg, numtostr(linenum), "(a)\n");
  500.                        } else {
  501.                                /*
  502.                                 *         This next bit checks to see if we are creating or
  503.                                 *         appending to the output file (i.e. >file or >>file)
  504.                                 */
  505.                                if (*++p == '>') {
  506.                                        p++;
  507.                                        append = YES;
  508.                                } else
  509.                                        append = NO;
  510.                                getname(p,filename,MODE_FILE);
  511.                                p = s;
  512.                                while ((p = index(p,'<',1)) && (p[1] != '<'))
  513.                                        ;
  514.                                if (p)
  515.                                        getname(p+2,endmarker,MODE_TEXT);
  516.  
  517.                                endlen = strlen(endmarker);
  518.  
  519.                                if (strlen(filename) && endlen) {
  520.  
  521.                                        checkfordir(filename);
  522.  
  523.                                        /* Found a valid line so perform extract */
  524.  
  525.                                        /* Check if file exists */
  526.  
  527.                                        skip = NO;
  528.                                        if (!overwrite) {
  529.                                                filelock = Lock(filename, ACCESS_READ);
  530.                                                if (filelock) {
  531.                                                        UnLock(filelock);
  532.                                                        if (!append) {
  533.                                                                print3("Overwrite file ", filename,
  534.                                                                        " (Yes, [No], All)? ");
  535.  
  536.                                                                switch (tolower(*input())) {
  537.  
  538.                                                                case 'a':
  539.                                                                        overwrite = YES;
  540.                                                                        break;
  541.                                                                case 'y':
  542.                                                                        skip = NO;
  543.                                                                        break;
  544.                                                                default:
  545.                                                                        skip = YES;
  546.                                                                        break;
  547.                                                                }
  548.                                                        }
  549.                                                }
  550.                                        }
  551.  
  552.                                        /*
  553.                                         *              Open as old file and seek to the end if
  554.                                         *              appending AND the file already exists. If
  555.                                         *              it doesn't exist, then just open as new file.
  556.                                         */
  557.                                        if (filelock && append) {
  558.                                                outfile = Open(filename, MODE_READWRITE);
  559.                                                if (outfile)
  560.                                                        Seek(outfile, 0, OFFSET_END);
  561.                                        } else
  562.                                                outfile = Open(filename, MODE_NEWFILE);
  563.  
  564.                                        if (!outfile)
  565.                                                print3("Couldn't open file ",filename," for output\n");
  566.                                        else {
  567.                                                if (!skip) {
  568.                                                        if (filelock && append)
  569.                                                                print3("Extending file ", filename, "\n");
  570.                                                        else
  571.                                                                print3("Unsharing file ", filename, "\n");
  572.                                                }
  573.                                                s = getline();
  574.                                                err = NO;
  575.                                                while (s && strncmp(s,endmarker,endlen) && !CtrlC) {
  576.                                                        if (stripfirst && !strncmp(sedstring,s,sedlen))
  577.                                                                s += sedlen;
  578.                                                        if (!skip && (err = putline(s)))
  579.                                                                break;
  580.                                                        s = getline();
  581.                                                }
  582.                                                flushout();
  583.                                                if (err || diskerror)
  584.                                                        print(DiskMsg), err = YES;
  585.                                                Close(outfile);
  586.                                        }
  587.                                } else
  588.                                        print(ErrorMsg, numtostr(linenum), "\n");
  589.                        }
  590.                }
  591.        }
  592.        if (!err && !CtrlC)
  593.                if (found)
  594.                        print("Unshar done\n");
  595.                else
  596.                        print("No files to unshar\n");
  597.        Close(infile);
  598.        flushin();
  599.        return(err);
  600. }
  601.  
  602. /*
  603.  *             readheader()
  604.  *             ------------
  605.  *             Reads in the first few lines (actually 480 bytes) of filename, and
  606.  *             scans for a subject line. If the subject line is found, then
  607.  *             it is stored in subject (up to 100 chars in length), else a null
  608.  *             string is stored. The subject line is also examined, and a sequence
  609.  *             number determined. If the subject line starts with i or I, followed
  610.  *             by a number, then this is taken as the sequence number. Otherwise,
  611.  *             the first number after `Part' or `part' is uses. This sequence
  612.  *             number is returned in seqnum. I-type sequence numbers have 1000
  613.  *             added on to them first of all, to keep them seperated from 'part'
  614.  *             types.
  615.  *
  616.  *             The idea is that successive parts of a set of several shar files
  617.  *             will have increasing sequence numbers.
  618.  *
  619.  *             Zero is returned if an error occurred.
  620.  */
  621. int readheader(filename, subject, seqnum)
  622. char *filename, *subject;
  623. int *seqnum;
  624. {
  625.        static char buf[480];
  626.        BPTR file;
  627.        int len, i;
  628.        char *p;
  629.  
  630.        *subject = '\0';
  631.  
  632.        file = Open(filename, MODE_OLDFILE);
  633.        if (!file) {
  634.                print3("Can't open file ", filename, " for input\n");
  635.                return (0);
  636.        }
  637.  
  638.        len = Read(file, buf, 480);
  639.        Close(file);
  640.        if (len == -1) {
  641.                print3("Error reading header from file ", filename, "\n");
  642.                return (0);
  643.        }
  644.  
  645.        /*
  646.         *              Now analyse file for a Subject: line
  647.         */
  648.        for (i = 0; i < len; i++) {
  649.                if (buf[i] == '\n' && strnicmp(&buf[i+1], "Subject:", 8) == 0) {
  650.                        /*
  651.                         *              Copy subject line into subject string, ensuring
  652.                         *              it is properly terminated with a \n and \0
  653.                         */
  654.                        i++;
  655.                        strncpy(subject, buf + i + 9, 98);
  656.                        subject[98] = '\0';
  657.                        for (p = subject; *p; p++) {
  658.                                if (*p == '\n') {
  659.                                        break;
  660.                                }
  661.                        }
  662.                        *p++ = '\n';
  663.                        *p = '\0';
  664.                        /*
  665.                         *              Now scan new subject string looking for sequence number
  666.                         */
  667.                        p = subject;
  668.                        while (*p) {
  669.                                if (*p == 'i') {
  670.                                        *seqnum = atoi(p+1);
  671.                                        if (*seqnum != 0)
  672.                                                return 1;
  673.                                }
  674.                                if (strnicmp(p, "Part", 4) == 0) {
  675.                                        p += 4;
  676.                                        while (*p == ' ')
  677.                                                *p++;
  678.                                        *seqnum = atoi(p);
  679.                                        if (*seqnum != 0) {
  680.                                                *seqnum += 1000;
  681.                                                return 1;
  682.                                        }
  683.                                } else
  684.                                        p++;
  685.                        }
  686.                        *seqnum = 10000;
  687.                        return (1);
  688.                }
  689.        }
  690.        *seqnum = 10000;
  691.        return (1);
  692. }
  693.  
  694. /*
  695.  *             Start of mainline
  696.  */
  697. int main(argc,argv)
  698. int argc;
  699. char *argv[];
  700. {
  701.  
  702.        int i, numfiles;
  703.        int overwrite = NO, sortfiles = YES;
  704.        char **filenames;
  705.  
  706.        if ((argc == 1) || (*argv[1] == '?')) {
  707.                print(HelpMsg);
  708.                return (10);
  709.        }
  710.        while (argc > 1 && *argv[1] == '-') {
  711.                switch (tolower(argv[1][1])) {
  712.  
  713.                        case 'o':
  714.                                overwrite = YES;
  715.                                break;
  716.  
  717.                        case 'n':
  718.                                sortfiles = NO;
  719.                                break;
  720.  
  721.                        default:
  722.                                print(HelpMsg);
  723.                                return (10);
  724.                }
  725.                argc--; argv++;
  726.        }
  727.  
  728.        numfiles  = argc - 1;
  729.        filenames = &argv[1];
  730.  
  731.        if (!sortfiles) {
  732.                /*
  733.                 *              Just process files in the order they occur
  734.                 */
  735.                for (i = 0; i < numfiles && !CtrlC; i++) {
  736.                        if (unshar(filenames[i], NULL, numfiles > 1, overwrite) != 0)
  737.                                break;
  738.                }
  739.        } else {
  740.                /*
  741.                 *              Do a prescan through all the files, and then unshar them
  742.                 *              in the right order.
  743.                 */     
  744.                typedef struct SortRecord {
  745.                        struct  SortRecord *next;       /* Next record in list                          */
  746.                        int             index;                          /* Index into filenames [] array        */
  747.                        int             seqnum;                         /* Sequence number of file                      */
  748.                        char    name[100];                      /* Original subject line                        */
  749.                } SORT;
  750.  
  751.                SORT *filehdrs = NULL, *head = NULL, *cur;
  752.  
  753.                filehdrs = AllocMem(sizeof(SORT) * numfiles, 0);
  754.                if (!filehdrs) {
  755.                        print("Couldn't allocate memory to store file headers\n");
  756.                        goto endsort;
  757.                }
  758.  
  759.                for (i = 0; i < numfiles; i++) {
  760.                        int seqnum;
  761.                        SORT **ptr;
  762.  
  763.                        if (!readheader(filenames[i], filehdrs[i].name, &seqnum) || CtrlC)
  764.                                goto endsort;
  765.  
  766.                        /*
  767.                         *              Now insert name at correct position in linked list
  768.                         */
  769.                        for (ptr = &head; *ptr && (*ptr)->seqnum <= seqnum;
  770.                                                                                                ptr = &(*ptr)->next)
  771.                                ;
  772.                        filehdrs[i].next = *ptr;
  773.                        *ptr = &filehdrs[i];
  774.  
  775.                        filehdrs[i].seqnum = seqnum;
  776.                        filehdrs[i].index  = i;
  777.                }
  778.                /*
  779.                 *              Now we have a sorted list of files, so just walk down
  780.                 *              the list unsharing files as we go.
  781.                 */
  782.                for (cur = head; cur && !CtrlC; cur = cur->next) {
  783.                        if (unshar(filenames[cur->index], cur->name,
  784.                                                                                        numfiles > 1, overwrite) != 0)
  785.                                break;
  786.                }
  787. endsort:
  788.                if (filehdrs)
  789.                        FreeMem(filehdrs, sizeof(SORT) * numfiles);
  790.        }
  791.  
  792.        /*
  793.         *              All files handled, now just tidy up and exit. If CtrlC was
  794.         *              pressed, let the user know.
  795.         */
  796.        if (CtrlC)
  797.                print("^C\n");
  798. }
  799.